#include <ostream>
#include <iostream>
#include <stdio.h>
//#include <windows.h>
#include <string>
#include <sstream>
#include <vector>
#include "../SharedMethods/globals.h"
#include "../SharedMethods/Exceptions.h"

using namespace std;
void interpret_input (const char* fileName,const char* direction);
void WaitForAcknowledge(); 
bool WaitForSuccessOrFailure(SOCKET socket);

//user defined port number
#define REQUEST_PORT 4444;
int port=REQUEST_PORT;

//socket data types
SOCKET s;
SOCKADDR_IN sa;         // filled by bind
SOCKADDR_IN sa_in;      // fill with server info, IP, port

//buffer data types
char szbuffer[128];
char *buffer;
char buf;

int ibufferlen=0;
int ibytessent;
int ibytesrecv=0;

//host data types
HOSTENT *hp;
HOSTENT *rp;

char localhost[11],
     remotehost[128];

//other
HANDLE test;
DWORD dwtest;



int main(void){

	WSADATA wsadata;

	try 
	{

		if (WSAStartup(0x0202,&wsadata)!=0)
		{  
			cout<<"Error in starting WSAStartup()" << endl;
		}
		else 
		{
			buffer="WSAStartup was successful\n";   
			WriteFile(test,buffer,sizeof(buffer),&dwtest,NULL); 

			/* Display the wsadata structure */
			cout<< endl
				<< "wsadata.wVersion "       << wsadata.wVersion       << endl
				<< "wsadata.wHighVersion "   << wsadata.wHighVersion   << endl
				<< "wsadata.szDescription "  << wsadata.szDescription  << endl
				<< "wsadata.szSystemStatus " << wsadata.szSystemStatus << endl
				<< "wsadata.iMaxSockets "    << wsadata.iMaxSockets    << endl
				<< "wsadata.iMaxUdpDg "      << wsadata.iMaxUdpDg      << endl;
		}  


		//Display name of local host.

		gethostname(localhost,10);
		cout<<"Local host name is \"" << localhost << "\"" << endl;

		if((hp=gethostbyname(localhost)) == NULL) 
			throw "gethostbyname failed\n";

		//Ask for name of remote server

		cout << "please enter your remote server name :" << flush ;   
		cin >> remotehost ;

		//exit program if "quit" is typed in.
		if(_stricmp(remotehost, "quit") == 0)
			exit(0);

		cout << "Remote host name is: \"" << remotehost << "\"" << endl;

		if((rp=gethostbyname(remotehost)) == NULL)
			throw "remote gethostbyname failed\n";

		//Create the socket
		if((s = socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET) 
			throw "Socket failed\n";
		/* For UDP protocol replace SOCK_STREAM with SOCK_DGRAM */

		// create loop for mltiple connections

		//Specify server address for client to connect to server.
		memset(&sa_in,0,sizeof(sa_in));
		memcpy(&sa_in.sin_addr,rp->h_addr,rp->h_length);
		sa_in.sin_family = rp->h_addrtype;   
		sa_in.sin_port = htons(port);

		//Display the host machine internet address
		cout << "Connecting to remote host:";
		cout << inet_ntoa(sa_in.sin_addr) << endl;

		//Connect Client to the server
		if (connect(s,(LPSOCKADDR)&sa_in,sizeof(sa_in)) == SOCKET_ERROR)
			throw "connect failed\n";

		/* Have an open connection, so, server is 

		   - waiting for the client request message
		   - don't forget to append <carriage return> 
		   - <line feed> characters after the send buffer to indicate end-of file */

		//append client message to szbuffer + send.

		bool done = false; 
		string filename;
		string direction;
		cin.ignore(); 
		while (!done)     
		{
			cout << "Type name of the file to be transferred: ";
			getline (cin, filename);
			if(filename == "quit")
				exit(0);
			//cin >> fileName;
			cout << "Type the direction of transfer: ";
			//cin >> direction;
			getline (cin, direction);
			
			
			interpret_input(filename.c_str(), direction.c_str());
		
		}

		ibytessent=0;    
		ibufferlen = strlen(szbuffer);
		ibytessent = send(s,szbuffer,ibufferlen,0);
		if (ibytessent == SOCKET_ERROR)
			throw "Send failed\n";  
		else
			cout << "Message to server: " << szbuffer;

		//wait for reception of server response.
		ibytesrecv=0; 
		if((ibytesrecv = recv(s,szbuffer,128,0)) == SOCKET_ERROR)
			throw "Receive failed\n";
		else
			cout << "hip hip hoorah!: Successful message replied from server: " << szbuffer;      

	} // try loop

	//Display any needed error response.

	catch (char *str) { cerr<<str<<":"<<dec<<WSAGetLastError()<<endl;}

	//close the client socket
	closesocket(s);

	/* When done uninstall winsock.dll (WSACleanup()) and exit */ 
	WSACleanup();  
	return 0;
}

void interpret_input (const char* fileName, const char* direction)
{
	if (stricmp(direction, "put") ==0)
	{
		string strFilePath = fileName;
		//get the size of the file
		FILE * pFile;
		long size;
		

		pFile = fopen (strFilePath.c_str(), "rb");
		if (pFile==NULL) 
		{	
			cout << "File does not exist." << endl;
			return ;
		}
		fseek (pFile, 0, SEEK_END);   // non-portable
		size=ftell (pFile);
		fclose (pFile);

		std::ostringstream o;
		o << size;
		string instrBuff = string("put:") + string(fileName) + ":" + o.str();

		// put <filename> <size>
		send (s,instrBuff.c_str(), instrBuff.size (), 0);
		try
		{
			WaitForAcknowledge();
		}
		catch (char* ex)
		{
			cout << "Error: " << ex << endl;
			return;
		}
		
		//send over the file
		pFile = fopen (fileName, "rb");
		
		char* buff = new char [size];
		fread(buff,size, 1, pFile);
		send (s, buff, size, 0);
		fclose (pFile);
		
		//wait for success/failure from server
		try{
			WaitForSuccessOrFailure(s);
		}
		catch(ConnectionException e)
		{
			cerr << e.message() << endl;
			return;
		}
		catch(FailedToReceiveAcknowledgeException e)
		{
			cerr << "Failed to transfer file to server, possibly due to a problem on the server's side" << endl;
			return;
		}

		//Let user know that file was sent
		cout << fileName << " Sent." << endl;
		//TODO: receive folder contents
	}
	else if (stricmp(direction, "get") ==0)
	{

		//Send a message explaining that we want a file
		string instrBuff = "get:" + string(fileName) + ":";
		send(s, instrBuff.c_str(), instrBuff.size(), 0);
		cout << "Sent request to " << remotehost << ", waiting..." << endl;
		//Wait for a response of type: "put:<filename>:<size>"
		//or a response of "nofile", which means the file doesn't exist.
		char* szreply = new char[256];
		recv(s, szreply, 256, 0); 
		string reply = szreply;

		if(reply.compare(0, strlen("nofile"), "nofile") == 0)
		{
			cerr << "The file does not exist" << endl;
			return;
		}
		
		vector<string>* strList = split(szreply, ":");


		//return if the message doesn't have enough parameters, or the third parameter isn't a number
		if(strList->size() < 3 || !isnumber((*strList)[2].c_str()))
		{
			cerr << "Failed to retrieve file from server, format error on server end." << endl;
			char* msg = "wrong format: file should be sent as: put:<filename>:<size>";
			send(s, msg, strlen(msg), 0);
			return;
		}
		long fileSize = atoi((*strList)[2].c_str());
		//create properly sized buffer to receive file
		char* buff = new char[fileSize];

		//send a message saying: "I'm waiting for you to send the file"
		send(s, "OK", 2, 0);

		//wait for receipt of file
		int res = recv(s, buff, fileSize, 0);
		
		if (res==0) 
			return; // Connection has been closed
		
		else if (res==SOCKET_ERROR)
		{
		//	send(s, "fail", strlen("fail"), 0); //tell the server we failed to receive the file.
			throw "socket error while receiving file.";
		}
		//write file onto hard-drive.
		FILE* fp;
		errno_t errNo = fopen_s(&fp, (*strList)[1].c_str(), "wb");
		if(errNo != 0)
		{
		//	send(s, "fail", strlen("fail"), 0); //tell the server we failed to write the file.
			throw ("Unable to open file " + (*strList)[1] + "for writing").c_str();
		}
		fwrite(buff, fileSize, 1, fp);
		cout << "Received " << (*strList)[1].c_str() << "." << endl;
		
		send(s, "success", strlen("success"), 0);
		
	    //SendFolderContents();


		fclose(fp);
		delete buff;
	}

}

//supspend thread until the other party sends an acknowledgement
void WaitForAcknowledge()
{
	//wait for acknowledgement
	char temp[128];
		
	int res = recv(s, temp, 128, 0);
	if (res==0) 
		return; // Connection has been closed
		
	else if (res==SOCKET_ERROR)
	{
		throw "socket error while waiting for an acknowledgment.";
	}
	else
	{
		if(_stricmp(temp, "OK") != 0)
			throw "Failed to receive acknowledgement, something was received, but it was not the right characters";
	}
}


//suspend thread until the other party returns a "success" or "fail"
//returns true on success, false on fail
//If the returned message is neither of these, then throw a FailedToReceiveAcknowledgeException, 
// 
bool WaitForSuccessOrFailure(SOCKET socket)
{
	//wait for acknowledgement
	char temp[128];
		
	int res = recv(socket, temp, 128, 0);
	if (res==0) 
		throw ConnectionException("Connection has been closed");
		
	else if (res==SOCKET_ERROR)
	{
		throw "socket error while waiting for an acknowledgment.";
	}
	else
	{
		string msg = temp;
		//if(_stricmp(temp, "success") == 0)
		if(msg.compare(0, strlen("success"), "success") == 0)
			return true;
	//	else if(_stricmp(temp, "Fail") == 0)
		else if(msg.compare(0, strlen("fail"), "fail") == 0)
			return false;
		else
			throw FailedToReceiveAcknowledgeException(temp);
	}
}




//int send_file(int fd)
//{
//
//struct stat stat_buf;
//int size=stat_buf.st_size;
//
//// Send a completion string
//int n = send(s, " ",strlen(" "), 0 );
//char buf[10];
//
//// Receive an acknowledgemnt
//n = recv(s, buf, 10, 0 );
//return(0);
//}
//
//
//int receive_file(int size, const char* name)
//{
//		
//// Receive file and send acknowledgement
//char buf[10];
//n = recv(s, buf, 10, 0 );
//n = send(s,  "OK", strlen("OK"), 0 );
//std::cout<<"File received..."<<std::endl;
//
//return(0);
//while(size >= 0)
//{      
//   // buffer for storing incoming data
//   if(size>=1024)
//   {
//	   // receive chunk of data
//       n=recv(s, buf, 1024, 0 );
//
//       // send acknowledgement
//       n = send(s, "OK", strlen("OK"), 0 );
//
//   }
//   else
//   {
//       n = recv(s, buf, size, 0 );
//       buf[size]='\0';
//       n = send(s, "OK", strlen("OK"), 0 );
//   }  
//
//   size -= 1024;
//   }
//
//}


